﻿using AlgoritmoConstrutivo.entidades;
using Algoritmos;
using Entidades;
using Entidades.entidades;
using RandomKeyGenerator;
using Support;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace AlgoritmoConstrutivo
{
    public class Orquestrador2
    {

        private InfoFormatter output = new InfoFormatter();
        private String tipoDecoder;
        private int numeroNos; //numero de nos existente no graf
        private FabricaCromossomos fabricaRandomKeys; //fabrica de numeros aleatorios
        private Cromossomo[] populacao; //vetor de vetores de numeros aleatorios

        private const int LIMITE_MELHORA_MQ = 30;
        private const int LIMITE_PERTURBACAO_POPULACAO = 30;
        private int LIMITE_MELHORA_MQ_RESET = 500;

        private int qtdeGeracoesSemMelhoriaMq_FinalizarExecucao;
        private int qtdeGeracoesSemMelhoriaMq_FinalizarExecucaoPosDinamico;
        private int qtdeGeracoesSemMelhoriaMq_Config;
        private int qtdeGeracoesSemMelhoriaMq_Reset;

        private int tamChavesAleatorias;

        private List<Configuration2> configs;

        private List<Grafo> softwares = new List<Grafo>(); //o grafo que representa o software em si
        private List<Estatistica> estsPorGeracao = new List<Estatistica>(); //o grafo que representa o software em si
        private List<Estatistica> estsPorExecucao = new List<Estatistica>(); //o grafo que representa o software em si
        private List<EstatisticaConsolidado> estsPorConfiguracao = new List<EstatisticaConsolidado>(); //o grafo que representa o software em si


        private List<Estatistica> estsElite; //o grafo que representa o software em si

        private Decimal mqGeracaoAtual = -1;
        private Decimal mqGeracaoAnterior = -1;

        Algoritmo decoder;
        private BRKGA2 framework;

        public static bool POPULACAO_RECONFIGURADA;
        public static bool POPULACAO_RESETADA;
        public static bool TERMINAR_GERACAO = false;
  



         /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="numeroNos">Numero de nos do grafo que representa um software</param>
        /// <param name="software">Grafo que representa um software</param>
        public Orquestrador2(int numeroNos, List<Grafo> softwares, string tipoDecoder,int tamChavesAleatorias, string estrategiaAlocacao,List<Configuration2> cfgs)
        {
            this.tipoDecoder = tipoDecoder;
            this.configs = new List<Configuration2>();
            this.configs = cfgs;
            this.softwares = softwares;
            this.numeroNos = numeroNos;
            LIMITE_MELHORA_MQ_RESET = this.numeroNos * 2;

            this.tamChavesAleatorias = tamChavesAleatorias;
            this.populacao = new Cromossomo[this.tamChavesAleatorias];
            this.fabricaRandomKeys = new FabricaCromossomos(this.numeroNos);
          
        }


        public List<EstatisticaConsolidado> IniciarGeracoes()
        {
            List<Estatistica> stats = new List<Estatistica>();

            for (int cfg = 0; cfg < this.configs.Count; cfg++) //para cada configuração definida.
            {
                DirectoryInfo cfgFolder = new DirectoryInfo (Program.instanciaFolder.FullName + "\\cfg_" + cfg);
                FileInfo execFile = new FileInfo(cfgFolder.FullName + "\\execucao.csv");
                FileInfo resumoFile = new FileInfo(cfgFolder.FullName + "\\resumo.csv");

                this.configurarEstrategiaAlocacao(this.configs[cfg]);

                if (!cfgFolder.Exists)
                    cfgFolder.Create();
             
                //no execução está a informação de geração.
                File.AppendAllText(execFile.FullName, "MQ;MQ_BL;TEMPO_MELHOR_MQ;TEMPO_TOTAL;ULTIMA_GER_MELHORIA;MAX_GERA;NOS;CLUSTERS;CLUSTERS_UNI;CFG;SEED;MUTANTE;ELITE"+ Environment.NewLine);


             
                Console.WriteLine("Configuração " + cfg + Environment.NewLine);


                int numeroTotalGeracoes = 0;

                if (this.configs[0].ParadaDinamica)
                    numeroTotalGeracoes = 3000;
                else
                    numeroTotalGeracoes = this.configs[0].QtdeGeracoes;



                for (int execucao = 0; execucao < Program.MEDIA; execucao ++) //para cada configuração executaremos MEDIA vezes.
                {
                    //resetar as configurações de elite e mutante.
                    this.configs[cfg].EliteProb = 0.25;
                    this.configs[cfg].MutantProb = 0.05;

                    this.configs[cfg].PopulationElite = Math.Round(this.configs[cfg].EliteProb * this.configs[cfg].Population);
                    this.configs[cfg].PopulationMutant = Math.Round(this.configs[cfg].MutantProb * this.configs[cfg].Population);

                    Program.timerTotal.Start();
                    Console.WriteLine("    Execucao " + execucao + Environment.NewLine);

                    int seed = softwares[0].RetornarNumNos() * softwares[0].NumeroDependencias() * execucao + 1;

                    RandomNumberGenerator.ConfigureSeed(seed);
             
                    this.InicializarPopulacao(this.configs[cfg]);
                    this.mqGeracaoAnterior = new Decimal();
                    this.mqGeracaoAtual = new Decimal();

                    for (int geracao = 0; geracao < numeroTotalGeracoes; geracao++) //para cada execução serão executadas "numeroTotalGeracoes"
                    {

                  
                        Configuration2 cfgAux = new Configuration2();
                        cfgAux = this.configs[cfg];
                        cfgAux.Seed = seed;

                        this.DecodificarPopulacao(geracao, cfgAux);
                        this.GerarEstatisticas(geracao, cfgAux);
                        this.VerificarPerturbacoes(geracao, cfgAux);

                       
                  
                        this.GerarMemoriaIndividuosElite(geracao, cfgAux);
                        this.RegerarPopulacao(geracao, cfgAux);
                        this.ResetarSoftware(cfgAux);

                        if (TERMINAR_GERACAO == true)
                        {
                            TERMINAR_GERACAO = false;
                            qtdeGeracoesSemMelhoriaMq_FinalizarExecucao = 0;
                            this.qtdeGeracoesSemMelhoriaMq_Config = 0;
                            this.qtdeGeracoesSemMelhoriaMq_Reset = 0;
                            qtdeGeracoesSemMelhoriaMq_FinalizarExecucaoPosDinamico = 0;

                            break;
                        }
                    }


                    this.output.PersistirInformacoesGeracoes(this.estsPorGeracao, this.softwares[0].Nome, cfgFolder.FullName,execucao);
                
                    this.estsPorGeracao.Sort(); //ordena os resultados obtidos em todas as gerações.

 
                    this.RealizarBuscaLocal(cfg);
                    Program.timerTotal.Stop();

                    this.ResetarSoftware(0);
             
                    Estatistica primeiraGeracaoMelhorMq = this.GetPrimeiraEstatisticaComMelhorMq(this.estsPorGeracao);
                    primeiraGeracaoMelhorMq.NumTotalGeracoes = this.estsPorGeracao.Count;


                    primeiraGeracaoMelhorMq.TempoExecucao = (decimal)Program.timerTotal.ElapsedMilliseconds / 1000;

                    Program.timerTotal.Reset();


                    //escrevo no arquivo texto a melhor linha da primeira execução.
                    File.AppendAllText(execFile.FullName, primeiraGeracaoMelhorMq.ToString());
                    File.AppendAllText(execFile.FullName, Environment.NewLine);


                    this.estsPorExecucao.Add(primeiraGeracaoMelhorMq);
                }

                    this.estsPorExecucao.Sort();
                    EstatisticaConsolidado resumoPorCFG = this.obterInformacoesImportantesDaConfiguracao(this.estsPorExecucao,cfg);

                    this.estsPorConfiguracao.Add(resumoPorCFG);
                    File.AppendAllText(resumoFile.FullName, resumoPorCFG + Environment.NewLine);

                    this.estsPorExecucao = new List<Estatistica>();
            }

            this.estsPorConfiguracao.Sort();


            //verificar quais configurações geraram a melhor média.
           List<EstatisticaConsolidado> melhoresCfgs = this.ObterMelhoresConfiguracoes(this.estsPorConfiguracao, Program.instanciaFolder);

           return melhoresCfgs;
        }

        private void configurarEstrategiaAlocacao(Configuration2 configuration2)
        {
            if (this.decoder == null)
            {
                Assembly assembly = Assembly.Load("Algoritmos");
                Type t = assembly.GetType(this.tipoDecoder);
                this.decoder = (Algoritmo)Activator.CreateInstance(t);
            }

                this.decoder.ConfigurarEstratégiaAlocacao(configuration2.EstrategiaDecodificacao);
        }

        private List<EstatisticaConsolidado> ObterMelhoresConfiguracoes(List<EstatisticaConsolidado> estCons, DirectoryInfo instanciaFolder)
        {
            List<EstatisticaConsolidado> cfgs = new List<EstatisticaConsolidado>();

            FileInfo melhoresCfgs = new FileInfo(instanciaFolder + "\\melhoresCFGs.CSV");
            File.AppendAllText(melhoresCfgs.FullName,"CONFIGURACAO;MEDIA_MQ" +Environment.NewLine );


            if (estCons.Count == 1)
            {
                File.AppendAllText(melhoresCfgs.FullName, estCons[0].IdCfg + ";" + estCons[0].MediaMQ + Environment.NewLine);
                cfgs.Add(estCons[0]);
                return cfgs;

            }
            if (estCons[1].MediaMQ != estCons[0].MediaMQ) //so uma cfg tem a melhor media
            {
                File.AppendAllText(melhoresCfgs.FullName, estCons[0].IdCfg + ";" + estCons[0].MediaMQ + Environment.NewLine);
                cfgs.Add(estCons[0]);
                return cfgs;
            }

            else
            {
                File.AppendAllText(melhoresCfgs.FullName, estCons[0].IdCfg + ";" + estCons[0].MediaMQ + Environment.NewLine);
                File.AppendAllText(melhoresCfgs.FullName, estCons[1].IdCfg + ";" + estCons[1].MediaMQ + Environment.NewLine);

                cfgs.Add(estCons[0]);
                cfgs.Add(estCons[1]);
            }

            for (int l = 2; l < estCons.Count; l++)
            {
                if (estCons[l].MediaMQ == estCons[0].MediaMQ)
                {
                    File.AppendAllText(melhoresCfgs.FullName, estCons[l].IdCfg + ";" + estCons[l].MediaMQ + Environment.NewLine);
                    cfgs.Add(estCons[l]);
                }
                else
                    break;
            }

            return estCons;
        }

        private Decimal getStandardDeviation(List<Decimal> doubleList)
        {
            Decimal average = doubleList.Average(); //media

            Decimal sumOfDerivation = 0;

            foreach (Decimal value in doubleList)
            {
                Decimal difMedia = average - value;

                sumOfDerivation += (difMedia) * (difMedia);
            }
            Decimal sumOfDerivationAverage = sumOfDerivation / (doubleList.Count);

            return (Decimal)Math.Round(Math.Sqrt((double)sumOfDerivationAverage), 5);
        }  

        private EstatisticaConsolidado obterInformacoesImportantesDaConfiguracao(List<Estatistica> list, int cfg)
        {
            EstatisticaConsolidado estCons = new EstatisticaConsolidado();

            Decimal mediaMQ = 0, mediaTempoObterMq = 0 ,mediaTempoTotalExecucao = 0 , mediaMQDepoisBuscaLocal = 0;
            List<Decimal> mqsSd = new List<Decimal>();
          

            foreach (Estatistica est in list)
            {
                mqsSd.Add(est.Mq);

                mediaMQDepoisBuscaLocal += est.MqDepoisBuscaLocal;
                mediaMQ += est.Mq;

                mediaTempoObterMq += est.TempoObterMq;
                mediaTempoTotalExecucao += est.TempoExecucao;
            }

            Decimal media = (Decimal)Program.MEDIA;

            mediaMQ /= media;
            mediaMQDepoisBuscaLocal /= media;
            mediaTempoObterMq /= media;
            mediaTempoTotalExecucao /= media;

            mediaMQ = Math.Round(mediaMQ, 5);
            mediaTempoObterMq = Math.Round(mediaTempoObterMq, 5);
            mediaTempoTotalExecucao = Math.Round(mediaTempoTotalExecucao, 5);

            Decimal sdMq = this.getStandardDeviation(mqsSd);

            sdMq = Math.Round(sdMq, 5);

            estCons.MediaMQ = mediaMQ;
            estCons.MediaTempoObterMq = mediaTempoObterMq;
            estCons.MediaMQDepoisBuscaLocal = mediaMQDepoisBuscaLocal;
            estCons.MediaTempoTotal = mediaTempoTotalExecucao;
            estCons.StandardDeviation = sdMq;
            estCons.IdCfg = cfg;
           

      
            return estCons;

        }

        private Estatistica GetPrimeiraEstatisticaComMelhorMq(List<Estatistica> list)
        {

            Estatistica est = new Estatistica();
            est = list[0];

            Decimal melhorMQ = list[0].Mq;

      

            for (int j = 1; j < list.Count; j++)
            {
                if (list[j].Mq == melhorMQ)
                {
                    if (est.Geracao > list[j].Geracao)
                    {
                        est = list[j];
                    }
                }

                else
                {
                    break;
                }
            }

            est.MqDepoisBuscaLocal = list[0].MqDepoisBuscaLocal;

            return est;
        }

        /// <summary>
        /// Guarda os individuos elite desta geração para serem utilizados na próxima.
        /// </summary>
        /// <param name="geracao"></param>
        /// <param name="cfg"></param>
        private void GerarMemoriaIndividuosElite(int geracao,Configuration2 cfg) //usa o valor de elite pela primeira vez.
        {
            this.estsElite = new List<Estatistica>();
           
            for (int i = 0; i < cfg.PopulationElite && POPULACAO_RESETADA == false && POPULACAO_RECONFIGURADA == false; i++)

            {
                Estatistica est = new Estatistica();
                est.Cromossomo = new Cromossomo();
            
                Grafo GrafoSolucaoElite = this.softwares[i];

                this.CopiaCromossomo(est.Cromossomo, GrafoSolucaoElite.Cromossomo);
                est.Geracao = geracao;
                est.Mutante = cfg.MutantProb;
                est.Elite = cfg.EliteProb;
                est.Seed = cfg.Seed;

                est.Mq = GrafoSolucaoElite.Mq;
                est.NumClustersGerados = GrafoSolucaoElite.RetornarNumClusters();
                est.OrdemVisitacaoCabecasChave = GrafoSolucaoElite.OrdemAtribuicaoCabecasChave;

                est.NumClustersUnitarios = GrafoSolucaoElite.RetornaNumeroClustersUnitarios();
                est.IdOriginalCromossomo = GrafoSolucaoElite.IdOriginalCromossomo;
                est.TempoObterMq = GrafoSolucaoElite.TempoObterMq;


                this.estsElite.Add(est);

            }

            
        }

        private void RegerarPopulacao(int geracao, Configuration2 cfg)
        {
            if (POPULACAO_RESETADA == false)
            {
                this.framework = new BRKGA2(this.populacao, this.GeraListaIdsSofwaresPorMq(geracao,cfg), this.numeroNos, cfg); //TODO: está instanciando dentro de um loop. Avaliar esta mudança.
                this.populacao = this.framework.GenerateNewPopulation();
            }
        }

        private void DecodificarPopulacao (int geracao, Configuration2 cfg)
        {
            int individuo = 0;

            //realiza a copia dos cromossomos elite para nao ter que decodificá-los novamente.
            if ((this.estsElite != null && this.estsElite.Count!=0) && !POPULACAO_RECONFIGURADA && !POPULACAO_RESETADA)
            {
                for (int k = 0; k < cfg.PopulationElite; k++)
                {
                    this.softwares[k].Cromossomo = new Cromossomo();
                    this.softwares[k].Mq = this.estsElite[k].Mq;
                    this.softwares[k].IdOriginalCromossomo = k;
                    this.softwares[k].Seed = this.estsElite[k].Seed;

                    this.softwares[k].ConfigurarClusters(this.estsElite[k].NumClustersGerados);

                    this.softwares[k].QtdeClustersUnitarios = this.estsElite[k].NumClustersUnitarios;


                    this.CopiaCromossomo(this.softwares[k].Cromossomo, this.estsElite[k].Cromossomo);
                  
                    this.softwares[k].TempoObterMq = this.estsElite[k].TempoObterMq;
                    this.softwares[k].Mutante = this.estsElite[k].Mutante;
                    this.softwares[k].Elite = this.estsElite[k].Elite;

                    this.softwares[k].OrdemAtribuicaoCabecasChave = this.estsElite[k].OrdemVisitacaoCabecasChave;
                   


                }
                individuo = (int)cfg.PopulationElite;
            }
            else

                individuo = 0;

            //decodifica o resto da população
            for (; individuo < cfg.Population; individuo++)
            {
                decoder.Decoder(cfg.Population, this.softwares[individuo], this.populacao[individuo], individuo,cfg);

                this.SalvarInformacoesParaEstatistica(individuo);
                this.softwares[individuo].CalculaMQ();
                this.softwares[individuo].TempoObterMq =(Decimal)  Program.timerTotal.ElapsedMilliseconds/1000;
            }
        }

        private void InicializarPopulacao(Configuration2 cfg)
        {
            this.estsElite = new List<Estatistica>(); //se nao fizer isso nesse ponto, ao passar para uma nova cfg, ele aproveita os valores do ultimo loop
            this.populacao = new Cromossomo[this.tamChavesAleatorias]; //se nao fizer isso nesse ponto, ao passar para uma nova cfg, ele aproveita os valores do ultimo loop
            this.estsPorGeracao = new List<Estatistica>(); //se nao fizer isso nesse ponto, ao passar para uma nova cfg, ele aproveita os valores do ultimo loop

            for (int i = 0; i < this.populacao.Count(); i++)
         
            {
                this.populacao[i] = this.fabricaRandomKeys.GenerateRandomKeysWithValue();

                int numClusters = Cluster.GerarNumeroClusters(false, this.numeroNos,cfg); //neste ponto o número de clusters é definido.
                if (numClusters == 1)
                    numClusters = 3;

                this.populacao[i].Value = numClusters;
            }
        }

        private void RealizarBuscaLocal(int cfg)
        {
            Estatistica melhorMqDeTodasGeracoes = this.estsPorGeracao[0];

            this.softwares[0] = decoder.Decoder(this.populacao.Count(), this.softwares[0], melhorMqDeTodasGeracoes.Cromossomo,
             melhorMqDeTodasGeracoes.IdOriginalCromossomo, melhorMqDeTodasGeracoes.Cfg);

            this.softwares[0].CalculaMQ();

            if (melhorMqDeTodasGeracoes.Mq != this.softwares[0].Mq)
            {
                Console.WriteLine("Geracao: " + melhorMqDeTodasGeracoes.Geracao + " Individuo: " + melhorMqDeTodasGeracoes.IdOriginalCromossomo + " ERRO: O mesmo par de chaves gerou MQs distintos ou entao foram utilizados parem de chaves distintos. O MQ que veio da execução é: " + this.estsPorGeracao[0].Mq + "e o MQ calculado agora é " + this.softwares[0].Mq);
                throw new Exception("O mesmo par de chaves gerou MQs distintos ou entao foram utilizados parem de chaves distintos. O MQ que veio da execução é: " + this.estsPorGeracao[0].Mq + "e o MQ calculado agora é " + this.softwares[0].Mq);
            }


            this.decoder.BuscaLocal(melhorMqDeTodasGeracoes.Cromossomo, this.softwares[0]);

            this.softwares[0].CalculaMQ();

            Decimal difference = 0.005M;

            Decimal resul = melhorMqDeTodasGeracoes.Mq - this.softwares[0].Mq;

            if (resul < 0 && resul > difference)
            {
                throw new Exception("Na cfg " + cfg + " Busca local gerou um valor menor do que deveria. Antes da Busca:" + melhorMqDeTodasGeracoes.Mq + "Depois da Busca:" + this.softwares[0].Mq);
            }

            melhorMqDeTodasGeracoes.MqDepoisBuscaLocal = this.softwares[0].Mq;
        }


        #region Estatisticas geracao Cromossomos

        private void CopiaCromossomo(Cromossomo destino, Cromossomo origem)
        {
            destino.Chaves = new Gene[origem.Chaves.Count()];

            for (int i = 0; i < origem.Chaves.Count(); i++)
            {
             
                destino.Chaves[i] = new Gene();

                destino.Chaves[i].Indice = origem.Chaves[i].Indice;
                destino.Chaves[i].KeyCriterio = origem.Chaves[i].KeyCriterio;
                destino.Chaves[i].KeyOrdenacao = origem.Chaves[i].KeyOrdenacao;

            }

            destino.Value = origem.Value;
        }


        private void GerarEstatisticas(int geracaoCorrente, Configuration2 cfg)
        {
           Grafo SoftwareMaiorMq =  this.RetornarMaiorMqDaGeracao(geracaoCorrente);

            Estatistica est = new Estatistica();
            est.Cromossomo = new Cromossomo();

            this.CopiaCromossomo(est.Cromossomo, SoftwareMaiorMq.Cromossomo);
          
            est.Geracao = geracaoCorrente;

            est.Mutante = cfg.MutantProb;
            est.Elite = cfg.EliteProb;
            est.Cfg = cfg;
            est.NumNos = SoftwareMaiorMq.RetornarNumNos();
            est.Software = SoftwareMaiorMq;
            est.Mq = SoftwareMaiorMq.Mq;
            est.Seed = cfg.Seed;
          

         
          
            est.NumClustersGerados = SoftwareMaiorMq.RetornarNumClusters();

            est.NumClustersUnitarios = SoftwareMaiorMq.RetornaNumeroClustersUnitarios();

            //se vier do elite, nao vai conseguir pegar esse valor pela lista de clusters
            if (est.NumClustersUnitarios == 0 && SoftwareMaiorMq.QtdeClustersUnitarios!=-1 && SoftwareMaiorMq.QtdeClustersUnitarios!=0)
                est.NumClustersUnitarios = SoftwareMaiorMq.QtdeClustersUnitarios;


            est.IdOriginalCromossomo = SoftwareMaiorMq.IdOriginalCromossomo;
            est.TempoObterMq = SoftwareMaiorMq.TempoObterMq;

            if (est.Mq > this.mqGeracaoAnterior) //a variavel mqGeracaoAnterior é usada na perturbação.
                this.mqGeracaoAnterior = est.Mq;

            this.estsPorGeracao.Add(est);
        }

      
        private void SalvarInformacoesParaEstatistica(int individuo)
        {
            this.softwares[individuo].Cromossomo = new Cromossomo();
            this.CopiaCromossomo( this.softwares[individuo].Cromossomo, this.populacao[individuo]);
        }
  
     
       
        #endregion

        #region Perturbar Solucao

        private void VerificarPerturbacoes(int geracao, Configuration2 cfg)
        {
            POPULACAO_RECONFIGURADA = false;
            POPULACAO_RESETADA = false;

            if (this.mqGeracaoAnterior <= this.mqGeracaoAtual)
            {

                this.qtdeGeracoesSemMelhoriaMq_FinalizarExecucao++; // contador que irá decidir se para a execução ou nao depois de X gerações sem melhoria
                this.qtdeGeracoesSemMelhoriaMq_Config++; //contador que decide se reconfigura ou não os parametros de elite e mutante

                if (cfg.ParadaFixa) //para depois de X gerações sem melhoria
                {
                    if (this.qtdeGeracoesSemMelhoriaMq_FinalizarExecucao == cfg.ExitExecucoesSemMelhoria)
                    {
                        TERMINAR_GERACAO = true;
                        return;
                    }
                }

                else if (cfg.ParadaDinamica && geracao >= cfg.ExecucoesDinamica) //executa X execuções e só então passar a avaliar se para a execução após y gerações sem melhoria
                {
                    

                    if (++this.qtdeGeracoesSemMelhoriaMq_FinalizarExecucaoPosDinamico == cfg.ExecucoesPosDefault)
                    {
                        TERMINAR_GERACAO = true;
                        return;
                    }
                }

                this.qtdeGeracoesSemMelhoriaMq_Reset++;
            }

            else
            {
                this.mqGeracaoAtual = this.mqGeracaoAnterior;
                this.qtdeGeracoesSemMelhoriaMq_Config = 0;
                this.qtdeGeracoesSemMelhoriaMq_Reset = 0;
                this.qtdeGeracoesSemMelhoriaMq_FinalizarExecucao = 0;
                this.qtdeGeracoesSemMelhoriaMq_FinalizarExecucaoPosDinamico = 0;
            }     

            if (this.qtdeGeracoesSemMelhoriaMq_Config >= Orquestrador2.LIMITE_MELHORA_MQ)
            {                  
                if (cfg.AvaliarReconfigPopulacao)
                    this.ReconfigPopulacao(cfg);
            
                this.qtdeGeracoesSemMelhoriaMq_Config = 0;

            }

            if (this.qtdeGeracoesSemMelhoriaMq_Reset >= this.LIMITE_MELHORA_MQ_RESET)
            {
                if (cfg.AvaliarResetPopulacao)
                    this.ResetPopulacao(cfg);

                this.qtdeGeracoesSemMelhoriaMq_Reset = 0;
            }
        }

        private void ResetPopulacao(Configuration2 cfg)
        {
            this.InicializarPopulacao(cfg);
            POPULACAO_RESETADA = true;

        }

        private void ReconfigPopulacao(Configuration2 cfg)
        {

            double difference = Math.Abs(0.001);

            if (cfg.Combinado == 0) //elite crescente e o mutante decrescente.
            {

                if (!(Math.Abs(cfg.MutantProb - 0.05) <= difference))
                {
                    cfg.MutantProb -= 0.05;
                    cfg.PopulationMutant = Math.Round(cfg.MutantProb * cfg.Population);


                }
                else
                {
                    cfg.MutantProb = 0.3;
                    cfg.PopulationMutant = Math.Round(cfg.MutantProb * cfg.Population);
                }
      
                if (!(Math.Abs(cfg.EliteProb - 0.25) <= difference))
                {
                    cfg.EliteProb += 0.05;
                    cfg.PopulationElite = Math.Round(cfg.EliteProb * cfg.Population);
                }

                else
                {
                    cfg.EliteProb = 0.1;
                    cfg.PopulationElite = Math.Round(cfg.EliteProb * cfg.Population);
                }
            }

            else if (cfg.Combinado == 1) //elite descrecente / mutante crescente
            {
                if (!(Math.Abs(cfg.MutantProb - 0.3) <= difference))
                {
                    cfg.MutantProb += 0.05;                  
                    cfg.PopulationMutant = Math.Round(cfg.MutantProb * cfg.Population);
                }

                else
                {
                    cfg.MutantProb = 0.05;
                    cfg.PopulationMutant = Math.Round(cfg.MutantProb * cfg.Population);
                }

                if (!(Math.Abs(cfg.EliteProb - 0.1) <= difference))
                {
                    cfg.EliteProb -= 0.05;
                    cfg.PopulationElite = Math.Round(cfg.EliteProb * cfg.Population);
                }

                else
                {
                    cfg.EliteProb = 0.25;
                    cfg.PopulationElite = Math.Round(cfg.EliteProb * cfg.Population);
                }
            }

            else if (cfg.Combinado == 2) // elite crescente e mutante crescente
            {
                if (!(Math.Abs(cfg.MutantProb - 0.3) <= difference))
                {
                    cfg.MutantProb += 0.05;
                    cfg.PopulationMutant = Math.Round(cfg.MutantProb * cfg.Population);
                }

                else
                {
                    cfg.MutantProb = 0.05;
                    cfg.PopulationMutant = Math.Round(cfg.MutantProb * cfg.Population);
                }

                if (!(Math.Abs(cfg.EliteProb - 0.25) <= difference))
         
                {
                    cfg.EliteProb += 0.05;
                    cfg.PopulationElite = Math.Round(cfg.EliteProb * cfg.Population);
                }

                else
                {
                    cfg.EliteProb = 0.1;
                    cfg.PopulationElite = Math.Round(cfg.EliteProb * cfg.Population);
                }
            }

            else if (cfg.Combinado == 3) // elite descrecente e mutante descrecente
            {
                if (!(Math.Abs(cfg.MutantProb - 0.1) <= difference))
                {
                    cfg.MutantProb -= 0.05;
                    cfg.PopulationMutant = Math.Round(cfg.MutantProb * cfg.Population);
                }

                else
                {
                    cfg.MutantProb = 0.3;
                    cfg.PopulationMutant = Math.Round(cfg.MutantProb * cfg.Population);
                }

                if (!(Math.Abs(cfg.EliteProb - 0.1) <= difference))
                {
                    cfg.EliteProb -= 0.05;
                    cfg.PopulationElite = Math.Round(cfg.EliteProb * cfg.Population);
                }

                else
                {
                    cfg.EliteProb = 0.25;
                    cfg.PopulationElite = Math.Round(cfg.EliteProb * cfg.Population);
                }
            }

      

            POPULACAO_RECONFIGURADA = true;
        }

        #endregion

        #region outros

        private Grafo RetornarMaiorMqDaGeracao(int geracaoCorrente)
        {
            this.softwares.Sort();
            return this.softwares[0];
        }


        private void ResetarSoftware(int id)
        {
            this.softwares[id].LimpaGrafo();
        }

        private void ResetarSoftware(Configuration2 cfg)
        {
            for (int i = 0; i < cfg.Population; i++)
                this.softwares[i].LimpaGrafo();        
        }

        /// <summary>
        /// Gera uma lista de indices de individuos / cromossomos ordenadas pelo seu MQ.
        /// </summary>
        /// <param name="softwares"></param>
        /// <returns></returns>
        private int[] GeraListaIdsSofwaresPorMq(int geracao, Configuration2 cfg)
        {
            int[] idsPorMq = new int[cfg.Population];


            for (int i = 0; i < softwares.Count; i++)
                idsPorMq[i] = this.softwares[i].IdOriginalCromossomo;
            
            return idsPorMq;
        }

        #endregion

    }
}
